package com.example.paypallesson01.controller;

import com.example.paypallesson01.config.PaypalConfig;
import com.example.paypallesson01.config.PaypalPaymentIntent;
import com.example.paypallesson01.config.PaypalPaymentMethod;
import com.example.paypallesson01.service.PaypalService;
import com.example.paypallesson01.utils.URLUtils;
import com.paypal.api.payments.Plan;
import com.paypal.base.rest.APIContext;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


import com.paypal.api.payments.Links;
import com.paypal.api.payments.Payment;
import com.paypal.base.rest.PayPalRESTException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Enumeration;


/**
 * Created with IntelliJ IDEA.
 *
 * @Author: anlu
 * @Date: 2021/06/28
 * @Description:
 */
@Controller
@RequestMapping("/")
public class PaymentController {
    public static final String PAYPAL_CANCEL_URL = "pay/cancel";
    public static final String PAYPAL_SUCCESS_URL = "pay/success";

    private Logger log = LoggerFactory.getLogger(getClass());
    @Autowired
    private APIContext apiContext;
    @Autowired
    private PaypalService paypalService;
    @Autowired
    private PaypalConfig config;

    @RequestMapping(method = RequestMethod.GET)
    public String index(){
        return "index";
    }

    /**
     * 创建订单请求
     * 创建成功返回payment对象，保存本地OrderId和paymentId对应关系
     */
    @RequestMapping(method = RequestMethod.POST, value = "pay")
    public String pay(HttpServletRequest request){
        String cancelUrl = URLUtils.getBaseURl(request) + "/" + PAYPAL_CANCEL_URL;
        String successUrl = URLUtils.getBaseURl(request) + "/" + PAYPAL_SUCCESS_URL;
        try {
            Payment payment = paypalService.createPayment(
                    400.00,
                    "USD",
                    PaypalPaymentMethod.paypal,
                    PaypalPaymentIntent.sale,
                    "安路 description",
                    cancelUrl,
                    successUrl);
            for(Links links : payment.getLinks()){
                if(links.getRel().equals("approval_url")){
                    System.out.println("links.getHref()="+links.getHref());
                    return "redirect:" + links.getHref();
                }
            }
        } catch (PayPalRESTException e) {
            log.error(e.getMessage());
        }
        return "redirect:/";
    }

    /**
     * 失败回调
     *   触发回调情景：
     *     1、用户在支付页面点击取消支付
     *     2、用户支付成功后点击网页回退，再点击返回商家按钮触发
     *   判断是否用户主动取消付款逻辑：
     *     1、设置回调地址的时候拼接本地订单ID：?orderId=XXXX
     *     2、然后根据orderId查询paymentId，继而调用sdk查询订单支付状态
     *      * http://localhost:8080/pay/cancel?orderId=2020110200001&token=EC-70674489LL9806126
     */
    @RequestMapping(method = RequestMethod.GET, value = PAYPAL_CANCEL_URL)
    public String cancelPay(){
        return "cancel";
    }

    /**
     * 成功回调 + 支付 + PDT同步通知
     *    买家确认付款，执行支付并直接返回通知
     */
//    @RequestMapping(method = RequestMethod.GET, value = PAYPAL_SUCCESS_URL)
//    public String successPay(@RequestParam("paymentId") String paymentId, @RequestParam("PayerID") String payerId){
//        try {
//            Payment payment = paypalService.executePayment(paymentId, payerId);
//            if(payment.getState().equals("approved")){
//                return "success";
//            }
//        } catch (PayPalRESTException e) {
//            log.error(e.getMessage());
//        }
//        return "redirect:/";
//    }

    /**
     * 成功回调 + 支付 + PDT同步通知
     *    买家确认付款，执行支付并直接返回通知
     */
    @RequestMapping(method = RequestMethod.GET, value = PAYPAL_SUCCESS_URL)
    public String successPay(@RequestParam("paymentId") String paymentId, @RequestParam("PayerID") String payerId, @RequestParam("token") String token) {
        log.info("==========successPay===============================================================================");
        try {
            /**
             * 执行支付
             */
            Payment payment = paypalService.executePayment(paymentId, payerId);
            if (payment.getState().equals("approved")) {
                // 交易ID，transactionId
                String id = "";
                // 交易订单状态
                String state = "";
                // 交易时间
                String time = "";
                // 本地OrderId
                String custom = "";
                if (payment.getIntent().equals(PaypalPaymentIntent.authorize.toString())) {
                    id = payment.getTransactions().get(0).getRelatedResources().get(0).getAuthorization().getId();
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getAuthorization().getState();
                    time = payment.getTransactions().get(0).getRelatedResources().get(0).getAuthorization().getCreateTime();
                    custom = payment.getTransactions().get(0).getCustom();
                } else if (payment.getIntent().equals(PaypalPaymentIntent.sale.toString())) {
                    id = payment.getTransactions().get(0).getRelatedResources().get(0).getSale().getId();
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getSale().getState();
                    time = payment.getTransactions().get(0).getRelatedResources().get(0).getSale().getCreateTime();
                    custom = payment.getTransactions().get(0).getCustom();
                } else if (payment.getIntent().equals(PaypalPaymentIntent.order.toString())) {
                    id = payment.getTransactions().get(0).getRelatedResources().get(0).getOrder().getId();
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getOrder().getState();
                    time = payment.getTransactions().get(0).getRelatedResources().get(0).getOrder().getCreateTime();
                    custom = payment.getTransactions().get(0).getCustom();
                }
                log.info("PDT通知：交易成功回调");
                log.info("付款人账户：" + payment.getPayer().getPayerInfo().getEmail());
                log.info("支付订单Id {}", paymentId);
                log.info("支付订单状态state : " + payment.getState());
                log.info("交易订单Id：{}", id);
                log.info("交易订单状态state : " + state);
                log.info("交易订单支付时间：" + time);
                log.info("本地系统OrderId：{}", custom);
                log.info("=========================================================================================");
                return "success";
            }
        } catch (PayPalRESTException e) {
            // 如果同步通知返回异常，可根据paymentId 来查询刷新订单状态
            // 同时IPN异步通知也可以更新订单状态
            log.error(e.getMessage());
        }
        return "redirect:/";
    }


    /**
     * 查看已付款账单的状态
     */
    @RequestMapping(method = RequestMethod.GET, value = "test")
    @ResponseBody
    public String selectTransactionState(@RequestParam("paymentId") String paymentId) {
        log.info("=========================================================================================");
        String state = "未产生支付信息";
        String custom = "";
        try {
            Payment payment = Payment.get(apiContext, paymentId);
            if (payment.getTransactions().size() > 0 && payment.getTransactions().get(0).getRelatedResources().size() > 0) {
                // 交易订单
                if (payment.getIntent().equals(PaypalPaymentIntent.sale.toString())) {
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getSale().getState();
                    custom = payment.getTransactions().get(0).getCustom();
                    // 授权订单
                } else if (payment.getIntent().equals(PaypalPaymentIntent.authorize.toString())) {
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getAuthorization().getState();
                    custom = payment.getTransactions().get(0).getCustom();
                    // 授权订单
                } else if (payment.getIntent().equals(PaypalPaymentIntent.order.toString())) {
                    state = payment.getTransactions().get(0).getRelatedResources().get(0).getOrder().getState();
                    custom = payment.getTransactions().get(0).getCustom();
                }
            }
        } catch (PayPalRESTException e) {
            e.printStackTrace();
        }
        log.info("订单状态：{} ", state);
        log.info(custom);
        log.info("=========================================================================================");

        return state;
    }

    /**
     * IPN异步通知
     *   触发情景：
     *      1、买家支付成功
     *      2、卖家确认收取授权或订单款项
     *      3、卖家发放退款
     */
    @RequestMapping(method = RequestMethod.POST, value = "/notificationIPN", produces = "application/json;charset=utf-8")
    public void receivePaypalStatus(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("=========================================================================================");
        log.info("IPN通知：异步回调");
        PrintWriter out = response.getWriter();
        try {
            Enumeration<String> en = request.getParameterNames();
            /**
             * 修改订单状态
             *      保存失败则不验签，继续接受paypal异步回调直至保存成功【或者用MQ】
             */
            // 交易状态
            String paymentStatus = request.getParameter("payment_status").toUpperCase();
            // 交易时间
            String paymentDate = request.getParameter("payment_date");
            // 本地系统订单ID
            String custom = request.getParameter("custom");
            // transactionId
            String auth_id = request.getParameter("auth_id");
            // 当前回调数据id【具体逻辑查看 .md文档】
            String txnId = request.getParameter("txn_id");
            // 父id
            String parentTxnId = request.getParameter("parent_txn_id");
            // 收款人email
            String receiverEmail = request.getParameter("receiver_email");
            // 收款人id
            String receiverId = request.getParameter("receiver_id");
            // 付款人email
            String payerEmail = request.getParameter("payer_email");
            // 付款人id
            String payerId = request.getParameter("payer_id");
            // 交易金额
            String mcGross = request.getParameter("mc_gross");
            String item_name = request.getParameter("item_name");
            log.info("paymentStatus = " + paymentStatus);
            log.info("txnId = " + txnId);
            log.info("parentTxnId = " + parentTxnId);
            log.info("authId（transactionId）= " + auth_id);
            log.info("custom（orderId）= " + custom);
            log.info("request item_name= " + item_name);
            //log.info("request item_name= " + new String(request.getParameter("item_name").getBytes("ISO-8859-1"), "UTF-8"));
            //log.info("item_name= " + new String(item_name.getBytes("ISO-8859-1"), "UTF-8"));
            //log.info("URLDecoder item_name= " + new String(URLDecoder.decode(request.getParameter("item_name"), "ISO-8859-1")));
            /**
             * 验证
             *   作用：
             *     订单状态修改成功，告诉paypal停止回调
             *   实现：
             *     在原参数的基础上加cmd=_notify-validate，然后对https://www.sandbox.paypal.com/cgi-bin/webscr发起POST验证请求
             */
            String str = "cmd=_notify-validate";
            while (en.hasMoreElements()) {
                String paramName = en.nextElement();
                String paramValue = request.getParameter(paramName);
                //此处的编码一定要和自己的网站编码一致，不然会出现乱码，paypal回复的通知为"INVALID"
                str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue, "utf-8");
            }
            log.info("paypal传递过来的交易信息:" + str);// 建议在此将接受到的信息 str 记录到日志文件中以确认是否收到 IPN 信息
            URL url = new URL(config.getWebscr());
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");//设置 HTTP 的头信息
            PrintWriter pw = new PrintWriter(connection.getOutputStream());
            pw.println(str);
            pw.close();
            /**
             * 回复
             *    接受PayPal对验证的回复信息
             */
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String resp = in.readLine();
            in.close();
            resp = StringUtils.isEmpty(resp) ? "0" : resp;
            log.info("resp = " + resp);
            /**
             * 验证返回状态
             */
            if (PaypalConfig.PAYMENT_IPN_VERIFIED.equalsIgnoreCase(resp)) {
                /**
                 * 修改订单状态
                 * 根据订单状态paymentStatus确定当前回调的类型
                 */
                switch (paymentStatus) {
                    case PaypalConfig.PAYMENT_STATUS_PENDING:
                        // 商家待领取状态
                        break;
                    case PaypalConfig.PAYMENT_STATUS_VOIDED:
                        // 商家作废（30天以内，且必须是授权付款类型 或 订单付款类型），款项原路返回买家
                        break;
                    case PaypalConfig.PAYMENT_STATUS_COMPLETED:
                        // 商家领取 实际领取对象ID【授权付款 和 订单付款需要商家领取】
                        String captureId = txnId;
                        break;
                    case PaypalConfig.PAYMENT_STATUS_REFUNDED:
                        // 商家退款，需扣除费用
                        String refundId = txnId;
                        String captureId2 = parentTxnId;
                        break;
                }
            } else if (PaypalConfig.PAYMENT_IPN_INVALID.equalsIgnoreCase(resp)) {
                // 非法信息，可以将此记录到您的日志文件中以备调查
                log.error("IPN通知返回状态非法，请联系管理员，请求参数：" + str);
                log.error("Class: " + this.getClass().getName() + " method: " + Thread.currentThread().getStackTrace()[1].getMethodName());
                out.println("confirmError");
            } else {// 处理其他错误
                log.error("IPN通知返回状态非法，请联系管理员，请求参数：" + str);
                log.error("Class: " + this.getClass().getName() + " method: " + Thread.currentThread().getStackTrace()[1].getMethodName());
                out.println("confirmError");
            }
        } catch (Exception e) {
            log.error("IPN通知发生IO异常" + e.getMessage());
            log.error("Class: " + this.getClass().getName() + " method: " + Thread.currentThread().getStackTrace()[1].getMethodName());
            out.println("confirmError");
            e.printStackTrace();
        }
        out.flush();
        out.close();
        log.info("=========================================================================================");
    }

}
